package cn.jbt.api.admin.service.impl;

import cn.jbt.api.admin.entity.QSysDept;
import cn.jbt.api.admin.entity.QSysDeptHead;
import cn.jbt.api.admin.entity.SysDept;
import cn.jbt.api.admin.entity.SysDeptHead;
import cn.jbt.api.admin.filter.LoginInfoAccessContext;
import cn.jbt.api.admin.repository.SysDeptHeadRepository;
import cn.jbt.api.admin.repository.SysDeptRepository;
import cn.jbt.api.admin.repository.SysUserRepository;
import cn.jbt.api.admin.service.ISysDeptService;
import com.querydsl.core.types.ExpressionUtils;
import com.querydsl.core.types.Predicate;
import com.querydsl.jpa.impl.JPAQueryFactory;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

@Service
@Slf4j
@AllArgsConstructor
public class SysDeptServiceImpl implements ISysDeptService {

    private final JPAQueryFactory jpaQueryFactory;
    private final SysDeptRepository sysDeptRepository;
    private final SysDeptHeadRepository sysDeptHeadRepository;
    private final SysUserRepository sysUserRepository;

    @Override
    public List<SysDept> getAll(SysDept sysDept) {
        QSysDept qSysDept = QSysDept.sysDept;
        QSysDeptHead qSysDeptHead = QSysDeptHead.sysDeptHead;

        Predicate predicate = qSysDept.isNotNull().or(qSysDept.isNull());
        if (null != sysDept) {
            if (!StringUtils.isEmpty(sysDept.getName())) {
                predicate = ExpressionUtils.and(predicate,
                        qSysDept.name.contains(sysDept.getName()));
            }
            if (null != sysDept.getStatus()) {
                predicate = ExpressionUtils.and(predicate,
                        qSysDept.status.eq(sysDept.getStatus()));
            }
        }

        return jpaQueryFactory.selectFrom(qSysDept)
                .where(predicate)
                .orderBy(qSysDept.floor.asc(), qSysDept.orderby.asc(), qSysDept.id.asc())
                .fetch()
                .stream()
                .peek(sysDept_ -> sysDept_.setHeadIds(jpaQueryFactory.select(qSysDeptHead.userId)
                        .from(qSysDeptHead)
                        .where(qSysDeptHead.deptId.eq(sysDept_.getId()))
                        .fetch()))
                .collect(Collectors.toList());
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public SysDept add(SysDept sysDept) {
        sysDept.setCreateBy(LoginInfoAccessContext.getLoginInfo().getNickname());
        sysDept.setUpdateBy(sysDept.getCreateBy());
        sysDept = sysDeptRepository.save(sysDept);

        if (sysDept.getPid() > 0) {
            SysDept parent = sysDeptRepository.findFirstById(sysDept.getPid());
            if (null == parent) {
                throw new RuntimeException("父级未找到");
            }
            sysDept.setFloor(parent.getFloor() + 1);
            sysDept.setNode(parent.getNode() + sysDept.getId() + "/");
        } else {
            sysDept.setFloor(1);
            sysDept.setNode(sysDept.getId() + "/");
        }
        // update floor、node
        sysDept = sysDeptRepository.save(sysDept);

        // 关联负责人
        if (CollectionUtils.isNotEmpty(sysDept.getHeadIds())) {
            List<SysDeptHead> sysDeptHeads = new ArrayList<>();
            for (Long userId : sysDept.getHeadIds()) {
                sysDeptHeads.add(new SysDeptHead().setUserId(userId).setDeptId(sysDept.getId()));
            }
            sysDeptHeadRepository.saveAll(sysDeptHeads);
        }

        return sysDept;
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public SysDept edit(SysDept sysDept) {
        SysDept newSysDept = sysDeptRepository.findFirstById(sysDept.getId());
        if (null == newSysDept) {
            throw new RuntimeException("404 NotFound");
        }

        newSysDept.edit(sysDept);
        newSysDept.setUpdateBy(LoginInfoAccessContext.getLoginInfo().getNickname());
        newSysDept = sysDeptRepository.save(newSysDept);

        // 删除旧的
        sysDeptHeadRepository.deleteByDeptId(newSysDept.getId());
        sysDeptHeadRepository.flush();

        // 关联负责人
        if (CollectionUtils.isNotEmpty(sysDept.getHeadIds())) {
            List<SysDeptHead> sysDeptHeads = new ArrayList<>();
            for (Long userId : sysDept.getHeadIds()) {
                sysDeptHeads.add(new SysDeptHead().setUserId(userId).setDeptId(sysDept.getId()));
            }
            sysDeptHeadRepository.saveAll(sysDeptHeads);
        }

        return newSysDept;
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void del(List<Long> ids) {
        for (Long id : ids) {
            if (null != sysDeptRepository.findFirstByPid(id)) {
                throw new RuntimeException("请先删除下级");
            }

            sysUserRepository.updateDeptId(id);
        }

        sysDeptRepository.deleteAllByIdInBatch(ids);
        sysDeptHeadRepository.deleteByDeptIdIn(ids);
    }

    @Override
    public String getCascadeNamesById(long id, String delimiter) {
        SysDept sysDept = sysDeptRepository.findFirstById(id);
        if (null == sysDept) {
            return "";
        }

        List<Long> ids = Arrays.stream(sysDept.getNode().split("/"))
                .filter(StringUtils::isNotEmpty)
                .map(Long::parseLong)
                .toList();

        QSysDept qSysDept = QSysDept.sysDept;
        List<String> names = jpaQueryFactory.select(qSysDept.name)
                .from(qSysDept)
                .where(qSysDept.id.in(ids))
                .orderBy(qSysDept.floor.asc())
                .fetch();

        return String.join(delimiter, names);
    }
}
